
#ifndef __MY_DATA__
#define __MY_DATA__

#include <mutex>
#include "util.hpp"
#include <mysql/mysql.h>

namespace vod_ns
{

#define HOST "127.0.0.1"
#define USER "exer"
#define PASS "123456"
#define NAME "vod_sys"

    static MYSQL *MysqlInit()
    {
        MYSQL *mysql = ::mysql_init(nullptr);
        if (mysql == nullptr)
        {
            std::cout << "Init mysql instance error!" << std::endl;
            return nullptr;
        }
        if (::mysql_real_connect(mysql, HOST, USER, PASS, NAME, 0, nullptr, 0) == nullptr)
        {
            std::cout << "Connect mysql Server error!" << std::endl;
            mysql_close(mysql);
            return nullptr;
        }
        ::mysql_set_character_set(mysql, "utf8");
        return mysql;
    }

    static void MysqlDestroy(MYSQL *mysql)
    {
        if (mysql)
        {
            ::mysql_close(mysql);
        }
        return;
    }

    static bool MysqlQuery(MYSQL *mysql, const std::string &sql)
    {
        int ret = ::mysql_query(mysql, sql.c_str());
        if (ret != 0)
        {
            std::cout << sql << ": ";
            std::cout << ::mysql_error(mysql) << std::endl;
            return false;
        }
        return true;
    }

    class TableVideo
    {
    private:
#define INSERT_VIDEO "insert into tb_video values (null, '%s', '%s', '%s', '%s');"
#define UPDATE_VIDEO "update tb_video set name = '%s', info = '%s' where id = %d;"
#define DELETE_VIDEO "delete from tb_video where id = %d;"
#define SELECTALL_VIDEO "select * from tb_video;"
#define SELECTONE_VIDEO "select * from tb_video where id = %d;"
#define SELECTONE_LIKE "select * from tb_video where name like '%%%s%%';"

    public:
        TableVideo()
        {
            _mysql = MysqlInit();
            if (_mysql == nullptr)
                exit(-1);
        }

        bool Insert(const Json::Value &video)
        {
            // id, name, info, video, image
            std::string sql;
            sql.resize(4096 + video["info"].asString().size());
            snprintf(&sql[0], sql.capacity(), INSERT_VIDEO,
                     video["name"].asCString(),
                     video["info"].asCString(),
                     video["video"].asCString(),
                     video["image"].asCString());
            return MysqlQuery(_mysql, sql);
        }

        bool Update(int video_id, const Json::Value &video)
        {
            std::string sql;
            sql.resize(4096 + video["info"].asString().size());
            snprintf(&sql[0], sql.capacity(), UPDATE_VIDEO, video["name"].asCString(), video["info"].asCString(), video_id);
            return MysqlQuery(_mysql, sql);
        }

        bool Delete(int video_id)
        {
            std::string sql;
            sql.resize(4096);
            snprintf(&sql[0], sql.capacity(), DELETE_VIDEO, video_id);
            return MysqlQuery(_mysql, sql);
        }

        bool SelectAll(Json::Value *pvideos)
        {
            _mutex.lock(); // -------- lock start 保护查询与保存结果到本地的过程 ------
            bool ret = MysqlQuery(_mysql, SELECTALL_VIDEO);
            if (ret == false)
            {
                _mutex.unlock();
                return false;
            }
            MYSQL_RES *res = ::mysql_store_result(_mysql);
            _mutex.unlock(); // -------- lock end -----------------------------------
            if (res == nullptr)
            {
                std::cout << "mysql store result error!" << std::endl;
                return false;
            }
            int num_rows = ::mysql_num_rows(res);
            for (int i = 0; i < num_rows; ++i)
            {
                MYSQL_ROW row = ::mysql_fetch_row(res);
                Json::Value video;
                video["id"] = std::stoi(row[0]);
                video["name"] = row[1];
                video["info"] = row[2];
                video["video"] = row[3];
                video["image"] = row[4];
                pvideos->append(video);
            }
            ::mysql_free_result(res);
            return true;
        }

        bool SelectOne(int video_id, Json::Value *pvideo)
        {
            std::string sql;
            sql.resize(1024);
            snprintf(&(sql[0]), sql.capacity(), SELECTONE_VIDEO, video_id);

            _mutex.lock(); // -------- lock start 保护查询与保存结果到本地的过程 ------
            bool ret = MysqlQuery(_mysql, sql);
            if (ret == false)
            {
                _mutex.unlock();
                return false;
            }
            MYSQL_RES *res = ::mysql_store_result(_mysql);
            _mutex.unlock(); // -------- lock end -----------------------------------
            if (res == nullptr)
            {
                std::cout << "mysql store result error!" << std::endl;
                return false;
            }

            int num_rows = ::mysql_num_rows(res);
            if (num_rows != 1)
            {
                std::cout << "The result of the query is incorrect!" << std::endl;
                ::mysql_free_result(res);
                return false;
            }
            MYSQL_ROW row = ::mysql_fetch_row(res);
            (*pvideo)["id"] = std::stoi(row[0]);
            (*pvideo)["name"] = row[1];
            (*pvideo)["info"] = row[2];
            (*pvideo)["video"] = row[3];
            (*pvideo)["image"] = row[4];
            ::mysql_free_result(res);
            return true;
        }

        bool SelectLike(const std::string &key, Json::Value *pvideos)
        {
            std::string sql;
            sql.resize(1024 + key.size());
            snprintf(&(sql[0]), sql.capacity(), SELECTONE_LIKE, key.c_str());

            _mutex.lock(); // -------- lock start 保护查询与保存结果到本地的过程 ------
            bool ret = MysqlQuery(_mysql, sql);
            if (ret == false)
            {
                _mutex.unlock();
                return false;
            }
            MYSQL_RES *res = ::mysql_store_result(_mysql);
            _mutex.unlock(); // -------- lock end -----------------------------------
            if (res == nullptr)
            {
                std::cout << "mysql store result error!" << std::endl;
                return false;
            }

            int num_rows = ::mysql_num_rows(res);
            for (int i = 0; i < num_rows; ++i)
            {
                MYSQL_ROW row = ::mysql_fetch_row(res);
                Json::Value video;
                video["id"] = std::stoi(row[0]);
                video["name"] = row[1];
                video["info"] = row[2];
                video["video"] = row[3];
                video["image"] = row[4];
                pvideos->append(video);
            }
            ::mysql_free_result(res);
            return true;
        }

        ~TableVideo()
        {
            MysqlDestroy(_mysql);
        }

    private:
        MYSQL *_mysql;
        std::mutex _mutex;
    };
}

#endif
